/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.inspur.edp.cef.core.buffer.base;

import com.inspur.edp.cef.api.RefObject;
import com.inspur.edp.cef.api.buffer.ICefBuffer;
import com.inspur.edp.cef.api.dataType.base.IAccessorCreator;
import com.inspur.edp.cef.api.manager.IBufferManager;
import com.inspur.edp.cef.api.session.ICefSession;
import com.inspur.edp.cef.api.session.ICefSessionItem;
import com.inspur.edp.cef.core.manager.EntityDataManager;
import com.inspur.edp.cef.entity.accessor.base.IAccessor;
import com.inspur.edp.cef.entity.changeset.AddChangeDetail;
import com.inspur.edp.cef.entity.changeset.IChangeDetail;
import com.inspur.edp.cef.entity.dependenceTemp.DataValidator;
import com.inspur.edp.cef.entity.entity.ICefData;
import com.inspur.edp.cef.entity.entity.IEntityData;
import java.util.Map;

public abstract class BufferManager implements IBufferManager {

  private ICefSession privateSession;

  protected final ICefSession getSession() {
    return privateSession;
  }

  private IAccessorCreator privateAccessorCreator;

  public final IAccessorCreator getAccessorCreator() {
    return privateAccessorCreator;
  }

  private String privateEntityType;

  protected final String getEntityType() {
    return privateEntityType;
  }

  protected BufferManager(ICefSession session, String entityType,
      IAccessorCreator accessorCreator) {
    DataValidator.checkForNullReference(session, "session");
    DataValidator.checkForEmptyString(entityType, "entityType");
    DataValidator.checkForNullReference(accessorCreator, "accessorCreator");

    privateSession = session;
    privateEntityType = entityType;
    privateAccessorCreator = accessorCreator;
  }

  public final IAccessor initBufferByLevel(String id, int level, boolean isReadonly) {
    throw new UnsupportedOperationException();
  }

  public final IAccessor initBuffer_ZeroLevel(IEntityData data, boolean isReadonly) {
    java.util.Map<String, java.util.Map<Integer, ICefBuffer>> buffers = getBuffers(getEntityType());

    CefBuffer result = new CefBuffer(createAccessor(data, isReadonly));
    java.util.HashMap<Integer, ICefBuffer> map = new java.util.HashMap<Integer, ICefBuffer>();
    map.put(0, result);
    buffers.put(data.getID(), map);

    return result.getData();
  }

  public final void rejectBufferZeroLevel(String id) {
    DataValidator.checkForNullReference(id, "id");

    java.util.Map<String, java.util.Map<Integer, ICefBuffer>> buffers = getBuffers(getEntityType());
    buffers.remove(id);
  }

  public final IAccessor initBufferByBaseLevel(String id, int level, boolean isReadonly) {
    DataValidator.checkForEmptyString(getEntityType(), "entityType");
    DataValidator.checkForEmptyString(id, "id");
    if (level <= 0) {
      throw new UnsupportedOperationException("level:" + level + "");
    }

    java.util.Map<Integer, ICefBuffer> buffer = getBuffers(getEntityType()).get(id);

    ICefBuffer upperbuffer = getMaxLevelBuffer(buffer);

    CefBuffer result = new CefBuffer(createAccessor(upperbuffer.getData(), isReadonly));
    buffer.put(level, result);

    return result.getData();
  }

  private ICefBuffer getMaxLevelBuffer(java.util.Map<Integer, ICefBuffer> buffer) {
    int value = 0;
    for (Map.Entry<Integer, ICefBuffer> enrity : buffer.entrySet()) {
			if (enrity.getKey() > value) {
				value = enrity.getKey();
			}
    }
    return buffer.get(value);
  }

  private Integer getMaxLevel(java.util.Map<Integer, ICefBuffer> buffer) {
    int value = 0;
    for (Map.Entry<Integer, ICefBuffer> enrity : buffer.entrySet()) {
			if (enrity.getKey() > value) {
				value = enrity.getKey();
			}
    }
    return value;
  }

  public final IAccessor initBufferByLevel(IEntityData data, int level, boolean isReadonly) {
    DataValidator.checkForNullReference(data, "data");
    DataValidator.checkForEmptyString(data.getID(), "data.ID");

    java.util.Map<String, java.util.Map<Integer, ICefBuffer>> buffers = getBuffers(getEntityType());
    java.util.Map<Integer, ICefBuffer> existing = buffers.get(data.getID());
		if (existing != null && existing.isEmpty() == false) {
			throw new UnsupportedOperationException();
		}

    CefBuffer buffer = new CefBuffer(createAccessor(data, isReadonly));
    java.util.HashMap<Integer, ICefBuffer> levels = new java.util.HashMap<Integer, ICefBuffer>();
    levels.put(level, buffer);
    buffers.put(data.getID(), levels);
    return buffer.getData();
  }

  private CefBuffer getCefBuffer(String dataId, int level) {
    DataValidator.checkForEmptyString(dataId, "dataId");
    ICefBuffer buffer = null;
    java.util.Map<String, java.util.Map<Integer, ICefBuffer>> buffers = getBuffers(getEntityType());
    java.util.Map<Integer, ICefBuffer> i = buffers.get(dataId);
		if (i != null) {
			return (CefBuffer) i.get(level);
		} else {
			return null;
		}
  }

  public final IAccessor getBuffer(String dataId, int level) {
    CefBuffer cefBuffer = getCefBuffer(dataId, level);
		if (cefBuffer != null) {
			return cefBuffer.getData();
		}
    return null;
  }

  public final IAccessor getBuffer(String dataId) {
    DataValidator.checkForEmptyString(getEntityType(), "entityType");
    DataValidator.checkForEmptyString(dataId, "dataId");

    java.util.Map<String, java.util.Map<Integer, ICefBuffer>> buffers = getBuffers(getEntityType());
    java.util.Map<Integer, ICefBuffer> buffer = buffers.get(dataId);
		if (buffer == null) {
			return null;
		}
    return getMaxLevelBuffer(buffer).getData();
  }

  public final IAccessor acceptChange(String dataId, int level, IChangeDetail change) {
    DataValidator.checkForEmptyString(dataId, "dataId");
    DataValidator.checkForNullReference(change, "change");

    java.util.Map<Integer, ICefBuffer> buffers = getBuffers(getEntityType()).get(dataId);

    Integer maxKey = getMaxLevel(buffers);
    if (maxKey != level) {
      throw new UnsupportedOperationException("level" + level + "只能往最外层buffer合并变更");
    }

    IAccessor rez;
    switch (change.getChangeType()) {
      case Modify:
        rez = buffers.get(maxKey).getData();
        rez.acceptChange(change);
        break;
      case Deleted:
        ((CefBuffer) buffers.get(maxKey)).setData(null);
        rez = null;
        break;
      case Added:
        CefBuffer maxBuffer = (CefBuffer) buffers.get(maxKey);
        if (maxBuffer.getData() != null) {
          throw new RuntimeException();
        }
        rez = createAccessor((IAccessor) ((AddChangeDetail) change).getEntityData().copy(),
            maxBuffer.getIsReadonly());
        ((CefBuffer) buffers.get(maxKey)).setData(rez);
        break;
      default:
        throw new UnsupportedOperationException(
            "change.ChangeType" + String.valueOf(change.getChangeType())
                + "ChangeType Not Supported");
    }
    return rez;
  }

  public final IAccessor accept(String dataId, int level, IChangeDetail change) {
    DataValidator.checkForEmptyString(dataId, "dataId");
    DataValidator.checkForNullReference(change, "change");
    if (level == 0) {
      throw new UnsupportedOperationException("level" + level + "");
    }

    IEntityData sourceAccessor = (IEntityData) getBuffer(dataId, level);
    IEntityData targetAccessor = (IEntityData) getBuffer(dataId, level - 1);
    RefObject<IEntityData> tempRef_targetAccessor = new RefObject<IEntityData>(targetAccessor);
    EntityDataManager.getInstance()
        .acceptChanges(getAccessorCreator(), sourceAccessor, tempRef_targetAccessor, change);
    targetAccessor = tempRef_targetAccessor.argvalue;

    getBuffers(getEntityType()).get(dataId)
        .put(level - 1, new CefBuffer((IAccessor) targetAccessor));
    return (IAccessor) targetAccessor;
  }

  public final IAccessor reject(String dataId, int level, IChangeDetail change) {
    DataValidator.checkForEmptyString(dataId, "dataId");
    DataValidator.checkForNullReference(change, "change");
    if (level == 0) {
      throw new UnsupportedOperationException("level" + level + "");
    }

    CefBuffer sourceBuffer = getCefBuffer(dataId, level);
    IEntityData sourceData = (IEntityData) sourceBuffer.getData();
    IAccessor targetAccessor = getBuffer(dataId, level - 1);
    RefObject<IEntityData> tempRef_sourceData = new RefObject<IEntityData>(sourceData);
    EntityDataManager.getInstance()
        .rejectChanges(getAccessorCreator(), tempRef_sourceData, (IEntityData) targetAccessor,
            change, sourceBuffer.getIsReadonly());
    sourceData = tempRef_sourceData.argvalue;
    getBuffers(getEntityType()).get(dataId).put(level, new CefBuffer((IAccessor) sourceData));

    return (IAccessor) sourceData;
  }

  public final void remove(String dataId) {
    java.util.Map<String, java.util.Map<Integer, ICefBuffer>> buffers = getBuffers(getEntityType());
    buffers.remove(dataId);
  }

  protected java.util.Map<String, java.util.Map<Integer, ICefBuffer>> getBuffers(
      String entityType) {
    ICefSessionItem sessionItem = getSession().getSessionItems().get(entityType);
    if (sessionItem == null) {
      throw new RuntimeException();
    }
    return sessionItem.getBuffers();

  }

  protected IAccessor createAccessor(ICefData data, boolean isReadonly) {
    //tfs453708
    if (data instanceof IAccessor && ((IAccessor) data).getIsReadonly()
        && ((IAccessor) data).getInnerData() != null) {
      data = ((IAccessor) data).getInnerData();
    }
		if (isReadonly) {
			return getAccessorCreator().createReadonlyAccessor((data));
		}
    return getAccessorCreator().createAccessor(data);
  }
}
